﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using GerbilLib;
using GerbilMigrate;

namespace MediaGerbil
{
    /// <summary>
    /// Displays the Preferences Dialog to the user to make changes to the settings MediaGerbil uses.
    /// </summary>
    public partial class PreferencesDialog : Form
    {
        /// <summary>
        /// Gets or sets the preferences.
        /// </summary>
        /// <value>
        /// The preferences.
        /// </value>
        private Preferences Preferences { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="PreferencesDialog"/> class.
        /// </summary>
        /// <param name="preferences">The preferences.</param>
        public PreferencesDialog(Preferences preferences = null)
        {
            InitializeComponent();
            
            Preferences = preferences;
            InitializeFromPreferences();
            UpdateLists();
        }

        #region .   Preferences Dialog Methods   .
        /// <summary>
        /// Initializes the dialog dropdowns, checkboxes etc. from the saved preferences.
        /// </summary>
        private void InitializeFromPreferences()
        {
            // Title Matching Tab
            cmbTitleMatchingSource.SelectedIndex = (int)Preferences.TitleMatchingOptions.TitleMatchSource;
            if (cmbTitleMatchingSource.SelectedIndex == -1)
                cmbTitleMatchingSource.SelectedIndex = 1;
            
            txtMatchThreshhold.Text = string.Format("{0:F}", Preferences.DiffMatchPatchMatchThreshhold);
            
            foreach (string extension in Preferences.MediaFileExtensions)
                lstFileExtensions.Items.Add(extension);

            rbStopAtFirstMatch.Checked = rbSortByMatchPosition.Checked = rbSortByLevenshtein.Checked = false;
            switch (Preferences.TitleMatchingOptions.TitleSortOption)
            {
                case TitleMatchingOptions.TitleSortOptions.StopAtFirstMatch:
                    rbStopAtFirstMatch.Checked = true;
                    break;
                case TitleMatchingOptions.TitleSortOptions.SortByPosition:
                    rbSortByMatchPosition.Checked = true;
                    break;
                case TitleMatchingOptions.TitleSortOptions.SortByLevenshtein:
                    rbSortByLevenshtein.Checked = true;
                    break;
            }
            chkSortByNewerYear.Checked = Preferences.TitleMatchingOptions.SortByNewerYear;
            chkUseYearInPath.Checked = Preferences.TitleMatchingOptions.UseYearInPath;
            chkUseYearInParentOnly.Checked = Preferences.TitleMatchingOptions.UseYearInParentOnly;

            // Title Cleaning Tab
            foreach (string pattern in Preferences.TitleCleaningOptions.IgnorePatterns)
                lstIgnoreStrings.Items.Add(pattern);
            chkStripYear.Checked = Preferences.TitleCleaningOptions.StripYearFromTitle;
            chkReplacePeriods.Checked = Preferences.TitleCleaningOptions.ReplacePeriodsWithSpaces;
            chkRemoveBrackets.Checked = Preferences.TitleCleaningOptions.RemoveBrackets;
            foreach (string directory in Preferences.IgnoredDirectories)
                lstIgnoreDirs.Items.Add(directory);

            // Cover Images Tab
            switch (Preferences.CoverImageOptions.CoverImageSource)
            {
                case CoverImageOptions.CoverImageSources.IMDb: rbImdbCover.Checked = true; break;
                case CoverImageOptions.CoverImageSources.TMDb: rbTmdbCover.Checked = true; break;
            }
            chkDontShowImagePicker.Checked = !Preferences.CoverImageOptions.AlwaysShowImagePicker;
            if (Preferences.CoverImageOptions.CoverImagePreviewSize == TmdbImage.ImageSize.Original) Preferences.CoverImageOptions.CoverImagePreviewSize = TmdbImage.ImageSize.Thumb;
            cmbCoverImagePickerPreviewSize.Text = GetImagePreviewSizeText(Preferences.CoverImageOptions.CoverImagePreviewSize);
            cmbCoverImagePickerDefaultSize.Text = GetImagePreviewSizeText(Preferences.CoverImageOptions.CoverImageDefaultSize);
            if (Preferences.CoverImageOptions.BackdropImagePreviewSize == TmdbImage.ImageSize.Original) Preferences.CoverImageOptions.BackdropImagePreviewSize = TmdbImage.ImageSize.Thumb;
            cmbBackdropImagePickerPreviewSize.Text = GetImagePreviewSizeText(Preferences.CoverImageOptions.BackdropImagePreviewSize);
            cmbBackdropImagePickerDefaultSize.Text = GetImagePreviewSizeText(Preferences.CoverImageOptions.BackdropImageDefaultSize);
        }

        /// <summary>
        /// Handles the Click event of the Ok button. Updates the Preferences object and saves them to disk.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnOk_Click(object sender, EventArgs e)
        {
            // Set new threshhold from text field
            float newThreshhold = 0.1f;
            float.TryParse(txtMatchThreshhold.Text, out newThreshhold);
            Preferences.DiffMatchPatchMatchThreshhold = newThreshhold;
            // Set title matching option from radio buttons
            Preferences.TitleMatchingOptions.TitleSortOption = (rbStopAtFirstMatch.Checked) ? TitleMatchingOptions.TitleSortOptions.StopAtFirstMatch : (rbSortByMatchPosition.Checked) ? TitleMatchingOptions.TitleSortOptions.SortByPosition : TitleMatchingOptions.TitleSortOptions.SortByLevenshtein;
            Preferences.TitleMatchingOptions.SortByNewerYear = chkSortByNewerYear.Checked;
            Preferences.TitleMatchingOptions.UseYearInPath = chkUseYearInPath.Checked;
            Preferences.TitleMatchingOptions.UseYearInParentOnly = chkUseYearInParentOnly.Checked;
            // Set cover image options
            Preferences.CoverImageOptions.AlwaysShowImagePicker = !chkDontShowImagePicker.Checked;
            Preferences.CoverImageOptions.CoverImagePreviewSize = GetImagePreviewSize(cmbCoverImagePickerPreviewSize.Text);
            Preferences.CoverImageOptions.CoverImageDefaultSize = GetImagePreviewSize(cmbCoverImagePickerDefaultSize.Text);
            Preferences.CoverImageOptions.CoverImageSource = (rbImdbCover.Checked) ? CoverImageOptions.CoverImageSources.IMDb : CoverImageOptions.CoverImageSources.TMDb;

            PreferencesHelper.SavePreferences(Preferences);
            this.Close();
        }
        #endregion

        #region .   Media Directories Preferences Methods   .
        /// <summary>
        /// Updates the media directories and excluded directories lists after making any changes to the
        /// underlying collections in the preferences.
        /// </summary>
        private void UpdateLists()
        {
            lstMediaDirs.BeginUpdate();
            lstMediaDirs.Items.Clear();
            lstMediaDirs.Items.AddRange(Preferences.MediaDirectories.ToArray());
            lstMediaDirs.EndUpdate();

            lstExcludedDirs.BeginUpdate();
            lstExcludedDirs.Items.Clear();
            lstExcludedDirs.Items.AddRange(Preferences.ExcludedMediaDirectories.ToArray());
            lstExcludedDirs.EndUpdate();
        }

        /// <summary>
        /// Displays the browse dialog for adding a new media directory.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnBrowse_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog fbd = new FolderBrowserDialog();
            fbd.SelectedPath = Application.StartupPath;
            fbd.Description = "Please pick the folder from which to recursively search for media files!";
            fbd.ShowNewFolderButton = false;
            if (fbd.ShowDialog() == DialogResult.OK)
            {
                AddPathToPreferences(Preferences.MediaDirectories, fbd.SelectedPath);
                UpdateLists();
            }
        }

        /// <summary>
        /// Adds the path to the media or excluded directories collection in the preferences.
        /// </summary>
        /// <param name="collection">The collection.</param>
        /// <param name="path">The path to the directory.</param>
        private void AddPathToPreferences(List<MediaDirectory> collection, string path)
        {
            if (collection.Count(x => x.Path == path) == 0)
                AddPathToPreferences(collection, new MediaDirectory(path, MediaType.Movie));
        }

        /// <summary>
        /// Adds the path to the media or excluded directories collection in the preferences.
        /// </summary>
        /// <param name="collection">The collection.</param>
        /// <param name="path">The path to the directory.</param>
        private void AddPathToPreferences(List<MediaDirectory> collection, MediaDirectory path)
        {
            if (!collection.Contains(path))
            {
                collection.Add(path);
                Logger.LogMessage(string.Format("Preferences: Added path {0} to media directories.", path), LoggingLevel.Info);
            }
        }

        /// <summary>
        /// Removes the path from the media or excluded directories collection in the preferences.
        /// </summary>
        /// <param name="collection">The collection.</param>
        /// <param name="path">The path to be removed.</param>
        private void RemovePathFromPreferences(List<MediaDirectory> collection, MediaDirectory path)
        {
            if (collection.Remove(path))
                Logger.LogMessage(string.Format("Preferences: Removed path {0} from media directories.", path), LoggingLevel.Info);
        }

        /// <summary>
        /// Adds the path the user entered into the textbox to the media directories collection.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnAdd_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(txtPath.Text.Trim()))
            {
                MessageBox.Show(this, "Please enter the Path to the Directory from which to recursively search for Media Files!", "No Media Directory Entered", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            AddPathToPreferences(Preferences.MediaDirectories, txtPath.Text.Trim());
            txtPath.Text = string.Empty;
            UpdateLists();
        }

        /// <summary>
        /// Removes the path(s) the user selected from the media directories collection.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnRemove_Click(object sender, EventArgs e)
        {
            List<MediaDirectory> selectedItems = GetSelectedItems(lstMediaDirs);
            foreach(MediaDirectory item in selectedItems)
                RemovePathFromPreferences(Preferences.MediaDirectories, item);
            UpdateLists();
        }

        /// <summary>
        /// Moves the selected path(s) from the media to the excluded directories list.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnExclude_Click(object sender, EventArgs e)
        {
            List<MediaDirectory> selectedItems = GetSelectedItems(lstMediaDirs);
            foreach(MediaDirectory item in selectedItems)
            {
                RemovePathFromPreferences(Preferences.MediaDirectories, item);
                AddPathToPreferences(Preferences.ExcludedMediaDirectories, item);
            }
            UpdateLists();
        }

        /// <summary>
        /// Removes the selected path(s) from the excluded directories collection.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnRemoveExcluded_Click(object sender, EventArgs e)
        {
            List<MediaDirectory> selectedItems = GetSelectedItems(lstExcludedDirs);
            foreach(MediaDirectory item in selectedItems)
                RemovePathFromPreferences(Preferences.ExcludedMediaDirectories, item);
            UpdateLists();
        }

        /// <summary>
        /// Moves the selected path(s) from the excluded to the media directories list.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnInclude_Click(object sender, EventArgs e)
        {
            List<MediaDirectory> selectedItems = GetSelectedItems(lstExcludedDirs);
            foreach (MediaDirectory item in selectedItems)
            {
                RemovePathFromPreferences(Preferences.ExcludedMediaDirectories, item);
                AddPathToPreferences(Preferences.MediaDirectories, item);
            }
            UpdateLists();
        }

        /// <summary>
        /// Switches the media type of the selected media directories between Movie and Series.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnChangeType_Click(object sender, EventArgs e)
        {
            List<MediaDirectory> selectedItems = GetSelectedItems(lstMediaDirs);
            foreach (MediaDirectory item in selectedItems)
            {
                if (item.MediaType == MediaType.Movie) item.MediaType = MediaType.Series;
                else item.MediaType = MediaType.Movie;
                lstMediaDirs.Invalidate();
            }
        }

        /// <summary>
        /// Gets the selected items from the checkbox list. If no checkbox has been checked,
        /// the currently highlighted item in the list wil be returned.
        /// </summary>
        /// <param name="list">The list.</param>
        /// <returns>List of selected items</returns>
        private List<MediaDirectory> GetSelectedItems(CheckedListBox list)
        {
            List<MediaDirectory> selectedItems = new List<MediaDirectory>();

            if (list.CheckedItems.Count == 0 && list.SelectedItem != null)
                selectedItems.Add(list.SelectedItem as MediaDirectory);
            else
                foreach (MediaDirectory item in list.CheckedItems)
                    selectedItems.Add(item);

            return selectedItems;
        }
        #endregion

        #region .   Title Matching Preferences Methods   .
        private void cmbTitleMatchingSource_SelectedIndexChanged(object sender, EventArgs e)
        {
            switch(cmbTitleMatchingSource.SelectedIndex)
            {
                case 0: // None
                    Preferences.TitleMatchingOptions.TitleMatchSource = TitleMatchingOptions.TitleMatchSources.None;
                    cmbTitleMatchingSource.Tag =
                        "When no source for local automated title matching is selected,\n" +
                        "MediaGerbil will go out to IMDb.com for every title when trying\n" +
                        "to match. This will be MUCH SLOWER than using a local file and\n" +
                        "most likely less accurate. This is NOT RECOMMENDED unless you\n" +
                        "have specific reason to use this option!";                    
                    break;
                case 1: // MPAA Ratings
                    Preferences.TitleMatchingOptions.TitleMatchSource = TitleMatchingOptions.TitleMatchSources.MpaaRatings;
                    cmbTitleMatchingSource.Tag = 
                        "This option uses the list of MPAA (Motion Picture Association\n" +
                        "of America) ratings with descriptions which is publicly available\n" +
                        "from the IMDb FTP server. The file is only 2MB in size and\n" +
                        "currently contains about 12,000 movies since 1995. This will\n" +
                        "work well if you have mostly recent American movies.";
                    break;
                case 2: // IMDb Ratings
                    Preferences.TitleMatchingOptions.TitleMatchSource = TitleMatchingOptions.TitleMatchSources.ImdbRatings;
                    cmbTitleMatchingSource.Tag =
                        "If you have a lot of older, foreign, or unrated (by the MPAA)\n" +
                        "movies this list might give you better results as it contains\n" +
                        "all movies that have an IMDb star rating. The file is 21MB in\n" +
                        "size and contains many more movies than the MPAA Ratings list.\n" +
                        "This should hit all but the most unusual movie titles!";
                    break;
                case 3: // Full Movie List
                    Preferences.TitleMatchingOptions.TitleMatchSource = TitleMatchingOptions.TitleMatchSources.FullMovieList;
                    cmbTitleMatchingSource.Tag =
                        "This is the full IMDb movie list with every single movie entry\n" +
                        "they have. The list is massive at 74MB and slow down title matching\n" +
                        "compared to the smaller lists, but will match any title that has\n" +
                        "an IMDb entry. This is not recommended unless you have specific\n" +
                        "reason to use this option!";
                    break;
            }
        }

        private void btnAddFileExtension_Click(object sender, EventArgs e)
        {
            if (!Preferences.MediaFileExtensions.Contains(txtFileExtension.Text))
            {
                Preferences.MediaFileExtensions.Add(txtFileExtension.Text);
                RefreshMediaFileExtensionsList();
            }
        }

        private void btnRemoveFileExtension_Click(object sender, EventArgs e)
        {
            foreach (string selectedItem in lstFileExtensions.SelectedItems)
                Preferences.MediaFileExtensions.Remove(selectedItem);
            RefreshMediaFileExtensionsList();
        }

        private void RefreshMediaFileExtensionsList()
        {
            lstFileExtensions.BeginUpdate();
            lstFileExtensions.Items.Clear();
            foreach (string extension in Preferences.MediaFileExtensions)
                lstFileExtensions.Items.Add(extension);
            lstFileExtensions.EndUpdate();
        }
        #endregion

        #region .   Title Cleaning Preferences   .
        private void btnAddIgnore_Click(object sender, EventArgs e)
        {
            AddIgnoreItem(Preferences.TitleCleaningOptions.IgnorePatterns, txtIgnoreString, lstIgnoreStrings);
            lblTestString_TextChanged(sender, e);
        }

        private void AddIgnoreItem(HashSet<string> set, TextBox textBox, ListBox listBox)
        {
            if (!set.Contains(textBox.Text))
            {
                set.Add(textBox.Text);
                textBox.Text = string.Empty;
                RefreshList(listBox, set);
            }
        }

        private void btnRemoveIgnore_Click(object sender, EventArgs e)
        {
            RemoveIgnoreItem(Preferences.TitleCleaningOptions.IgnorePatterns, lstIgnoreStrings);
            lblTestString_TextChanged(sender, e);
        }

        private void RemoveIgnoreItem(HashSet<string> set, ListBox listBox)
        {
            foreach (string selectedItem in listBox.SelectedItems)
                set.Remove(selectedItem);
            RefreshList(listBox, set);
        }

        private void btnAddIgnoreDir_Click(object sender, EventArgs e)
        {
            AddIgnoreItem(Preferences.IgnoredDirectories, txtIgnoreDir, lstIgnoreDirs);
        }

        private void btnRemoveIgnoreDir_Click(object sender, EventArgs e)
        {
            RemoveIgnoreItem(Preferences.IgnoredDirectories, lstIgnoreDirs);
        }

        private void RefreshList(ListBox listBox, HashSet<string> set)
        {
            listBox.BeginUpdate();
            listBox.Items.Clear();
            foreach (string pattern in set)
                listBox.Items.Add(pattern);
            listBox.EndUpdate();
        }

        private void lblTestString_TextChanged(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(lblTestString.Text))
            {
                lblCleanedString.Text = "Please enter a test string!";
                return;
            }

            lblCleanedString.Text = MediaItem.GetCleanTitle(lblTestString.Text, Preferences.TitleCleaningOptions);
        }

        private void chkReplacePeriods_CheckedChanged(object sender, EventArgs e)
        {
            Preferences.TitleCleaningOptions.ReplacePeriodsWithSpaces = chkReplacePeriods.Checked;
            lblTestString_TextChanged(sender, e);
        }

        private void chkStripYear_CheckedChanged(object sender, EventArgs e)
        {
            Preferences.TitleCleaningOptions.StripYearFromTitle = chkStripYear.Checked;
            lblTestString_TextChanged(sender, e);
        }

        private void chkRemoveBrackets_CheckedChanged(object sender, EventArgs e)
        {
            Preferences.TitleCleaningOptions.RemoveBrackets = chkRemoveBrackets.Checked;
            lblTestString_TextChanged(sender, e);
        }

        private void chkUseYearInPath_CheckedChanged(object sender, EventArgs e)
        {
            chkUseYearInParentOnly.Enabled = chkUseYearInPath.Checked;
            if (!chkUseYearInPath.Checked) chkUseYearInParentOnly.Checked = false;
        }

        private void btnAddDefaults_Click(object sender, EventArgs e)
        {
            TitleCleaningOptions defaults = Preferences.GetDefaultTitleCleaningOptions();
            Preferences.TitleCleaningOptions.IgnorePatterns.UnionWith(defaults.IgnorePatterns);
            RefreshList(lstIgnoreStrings, Preferences.TitleCleaningOptions.IgnorePatterns);
            lblTestString_TextChanged(sender, e);
        }
        #endregion

        #region .   Cover Images Preferences   .
        /// <summary>
        /// Gets the image preview size text.
        /// </summary>
        /// <param name="imageSize">Size of the image.</param>
        /// <returns>The image preview size text.</returns>
        private string GetImagePreviewSizeText(TmdbImage.ImageSize imageSize)
        {
            switch (imageSize)
            {
                case TmdbImage.ImageSize.Thumb: return "Thumbnail";
                case TmdbImage.ImageSize.Cover: return "Cover";
                case TmdbImage.ImageSize.Mid: return "Mid-Size";
                case TmdbImage.ImageSize.Poster: return "Poster";
                case TmdbImage.ImageSize.Original: return "Original";
                case TmdbImage.ImageSize.w1280: return "1280 Wide";
                case TmdbImage.ImageSize.w154: return "154 Wide";
                case TmdbImage.ImageSize.w342: return "342 Wide";
                default: return "Thumbnail";
            }
        }

        /// <summary>
        /// Gets the size of the image preview.
        /// </summary>
        /// <param name="imageSize">Text description of the size of the image.</param>
        /// <returns>The image preview size.</returns>
        private TmdbImage.ImageSize GetImagePreviewSize(string imageSize)
        {
            switch (imageSize)
            {
                case "Thumbnail": return TmdbImage.ImageSize.Thumb;
                case "Cover": return TmdbImage.ImageSize.Cover;
                case "Mid-Size": return TmdbImage.ImageSize.Mid;
                case "Poster": return TmdbImage.ImageSize.Poster;
                case "Original": return TmdbImage.ImageSize.Original;
                case "1280 Wide": return TmdbImage.ImageSize.w1280;
                case "154 Wide": return TmdbImage.ImageSize.w154;
                case "342 Wide": return TmdbImage.ImageSize.w342;
                default: return TmdbImage.ImageSize.Thumb;
            }
        }
        #endregion
    }
}
